## -*- coding: UTF-8 -*-
import os.path
import gtk
import shutil

from .. import editmetadata
from ... import collection as c
from ... import core
from ... import exceptions
from ... import html
from ... import genres
from ... import log
from ... import settings
from ... import path
from ... import strunicode as su
from ...plugin import find_plugin_for_file
from ...plugin import get_plugin
from ...plugin import construct_attr as ca

__book_popup_menu = None
__book_popup_menu_items = None

def initialize():
    """
    Initialize module: create required popup menus etc
    """
    global __book_popup_menu, __book_popup_menu_items
    
    book_popup_menu_items = (
        (None, _("Export book"), on_menu_export_book, _("Export book file into external directory")),
        (None, _("Edit description"), on_menu_edit_book_description, _("Edit book description in the database")),
        ("edit_book", _("Edit book file"), on_edit_book_file, _("Edit book file")),
        ("lock_book", "xxx", on_menu_set_book_lock, None),
        (None, _("Read book"), on_menu_read_book, _("Read book")),
        ("bookshelves_submenu", _("Bookshelves"), None, None),
        (None, "", None, None),
        ("add_bookmark", _("Bookmark book"), on_menu_bookmark_book, _("Bookmark this book")),
        )
    
    #__book_popup_menu, __book_popup_menu_items = core.create_menu(book_popup_menu_items)
    __book_popup_menu = gtk.Menu()
    __book_popup_menu_items = dict()
    for item_id, title, func, tooltip in book_popup_menu_items:
        mi = None
        
        if "" == title:
            mi = gtk.SeparatorMenuItem()
        elif func is None:
            # add submenu
            submenu = gtk.Menu()
            mi = gtk.MenuItem()
            label = gtk.Label(title)
            label.set_alignment(0, 0.5)
            mi.add(label)
            mi.set_data("label", label)
            mi.set_submenu(submenu)
        else:
            mi = gtk.MenuItem()
            label = gtk.Label(title)
            label.set_alignment(0, 0.5)
            mi.add(label)
            mi.connect("activate", func)
            mi.set_data("label", label)
            if tooltip is not None:
                mi.set_tooltip_text(tooltip)
            
        if not mi is None:
            __book_popup_menu.append(mi)
        
        if not item_id is None:
            __book_popup_menu_items[item_id] = mi 
    
    
    recreate_shelves_submenu()
    
    __book_popup_menu.show_all()
    
def recreate_shelves_submenu():
    
    shelves_submenu = __book_popup_menu_items['bookshelves_submenu'].get_submenu()

    # delete all items
    for mi in shelves_submenu.get_children():
        shelves_submenu.remove(mi)
        mi.destroy()
        
    for shelf, shelf_description in c.get_book_shelves():
        mi = gtk.MenuItem()
        box = gtk.HBox(homogeneous=False, spacing=4)
        label = gtk.Label(shelf)
        label.set_alignment(0, 0.5)
        icon = gtk.Image()
        icon.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
        box.pack_start(icon, expand=False, fill=True, padding=0)
        box.pack_start(label, expand=True, fill=True, padding=0)
        mi.set_data("icon", icon)
        mi.add(box)
        mi.set_data("shelf", shelf)
        mi.connect("activate", on_book_shelf_activated)
        shelves_submenu.append(mi)
    
    shelves_submenu.append(gtk.SeparatorMenuItem())
    mi = gtk.MenuItem(label = _(u"Add new shelf…"))
    mi.set_tooltip_text(_("Create new shelf and place book on it"))
    mi.connect("activate", on_book_shelf_new_activated)
    shelves_submenu.append(mi)
    shelves_submenu.show_all()
    
def render_book_page(link_text, generated_files_dir, arguments):
    """
    Generate book html page.
    
    @param link_text: db record id of the book
    @return: tuple(body, generated_files, page_title)
    """
    file_id = link_text
    
    # check that book file exists
    book_file = c.get_book_file(file_id)
    if book_file is None:
        core.error(message=_("Book with id `%s' not found in the database" % file_id))
        return True
    
    if not os.path.isfile(book_file):
        raise exceptions.BrowserError(_("Book file (%s) doesn't exist." % su.str2unicode(book_file)))
    
    plugin_id = find_plugin_for_file(book_file)
    if plugin_id is None:
        core.error(message=_("Format of the file `%s' is not supported" % book_file))
        return True
    
    # create block with ratings
    rating = c.get_book_rating(file_id)
    rating_stars = []
    for r in range(rating):
        rating_stars.append(True)
        
    for r in range(5-rating):
        rating_stars.append(False)

    rating_html = ""
    
    if rating != 0:
        # append icon "unrate book"
        rating_html += '''<a title="%(tooltip)s" onmouseover="rmor(event, %(mark)s)" onmouseout="rmou(event, %(mark)s, '%(state)s')"
          href="voteforbook://%(mark)s?id=%(file_id)s"><img id="rating_%(mark)s" src="%(img)s"></a>''' % {
            'file_id': file_id,
            'img': html.stock_img_url("star-over.png"),
            'mark': "0",
            'state': "1",
            'tooltip': _("Clear book rating")
            }
        
    orig_state = ""
    titles = html.get_rating_titles()
    
    for i in range(5):
        r = rating_stars[i]
        d = {
            'file_id': file_id,
            'mark': i+1,
            'tooltip': titles[i]
            }
        if r:
            d['img'] = html.stock_img_url("star.png")
            orig_state += "1"
        else:
            d['img'] = html.stock_img_url("star-bw.png")
            orig_state += "0"
        d['state'] = orig_state
            
        rating_html += '''<a title="%(tooltip)s" onmouseover="rmor(event, %(mark)s)" onmouseout="rmou(event, %(mark)s, '%(state)s')"
          href="voteforbook://%(mark)s?id=%(file_id)s"><img id="rating_%(mark)s" src="%(img)s"></a>''' % d

    plugin = get_plugin(plugin_id)
    body, files, book_title = plugin.gen_book_html(file_id, generated_files_dir, 
        presets={'rating': rating_html})
    
    return body, files, _(u"Book “%s”") % book_title


def render_genre_page(link_text, generated_files_dir, arguments):
    genre_name = link_text

    if 'author' in arguments:
        return render_genre_author_page(link_text, generated_files_dir, arguments)
    
    page_template = html.get_html_template("genre")
    author_template = html.get_html_template("genre_author")
    
    # keys of catalog are letters - first letters of the author's lastname (or firstname, middlename, nickname if 
    # all previous are empty)
    catalog = dict()
    
    for row in c.get_genre_authors_stat(genre_name):
        author_name, number = row
    
        try:
            cat_key = author_name[0]
        except IndexError:
            cat_key = "-"

        if not catalog.has_key(cat_key):
            catalog[cat_key] = []

        catalog[cat_key].append((author_name, number))

    authors = ""
    for cat_key in sorted(catalog.keys()):
        authors += u'<div class="header-2">%s</div>' % cat_key
        
        authors_list = []
        for author_name, books_num in catalog[cat_key]:
            author = author_template % {
                #'author_id': author_id,
                #'genre_id': genre_id,
                'author_name': author_name,
                'genre_name':  genre_name,
                'genre_name_enc': html.quote(genre_name),
                'author_name_enc': html.quote(author_name),
                'books_num': books_num
                }
            
            authors_list.append(author)

        authors += '<ul class="plain-list">%s</ul>' % "\n".join(authors_list)

    
    body = page_template % {
        'label_genre': _("Genre"),
        'genre': genres.expand_genre(genre_name),
        'authors': authors
        }

    return body, [], _(u"Genre “%s”") % genre_name
    
def render_genre_author_page(link_text, generated_files_dir, arguments):
    page_template = html.get_html_template("genre-author")
    book_template = html.get_html_template("genre-author_book")
    
    author_name = arguments['author']
    genre_name = link_text
    
    genre = genres.expand_genre(genre_name)

    books = []

    for file_id in c.get_genre_author_books(genre_name, author_name):
        book = book_template % {
            'file_id': file_id,
            'book_title': c.get_book_title(file_id)
            }
        books.append(book)

    body = page_template % {
        'author': author_name,
        'genre': genre,
        'books': "\n".join(books)
        }

    return body, [], _(u"Genre “%(genre)s”, %(author)s") % {'genre': genre, 'author': author_name}

def render_author_page(link_text, generated_files_dir, arguments):
    page_template = html.get_html_template("author")
    book_template = html.get_html_template("author_book")
    
    # now get list of author's books
    books = c.get_author_books(link_text)
    books_list = "\n".join([book_template % {'file_id': b[0], 'book_title': b[1]} for b in books])
    
    
    body = page_template % {
        'author': link_text, 
        'books': books_list,
        }

    return body, [], _(u"Author “%s”") % link_text

def render_series_page(link_text, generated_files_dir, arguments):
    
    page_template = html.get_html_template("series") 
    book_template = html.get_html_template("series_book")
    author_template = html.get_html_template("series_author")

    series_name = link_text
    books = c.get_series_books(series_name)
    series_books = []

    for file_id, pos_in_series in books:
        try:
            pos_in_series = int(pos_in_series)
        except ValueError:
            pos_in_series = 0
        except TypeError:
            pos_in_series = 0
        
        if pos_in_series == 0:
            pos_in_series = "?"

        _authors = []

        for author_name in c.get_book_authors(file_id):
            _authors.append(author_template % {
                'author_name': author_name
                })
        
        book_item = book_template % {
            'number': pos_in_series,
            'authors': ", ".join(_authors),
            'file_id': file_id,
            'book_title': c.get_book_title(file_id),
            }

        series_books.append((pos_in_series, book_item,))
    
    
    def sort_func(x, y):
        try:
            x = int(x[0])
        except TypeError:
            x = 0
        except ValueError:
            x = 0

        try:
            y= int(y[0])
        except TypeError:
            y = 0
        except ValueError:
            y = 0

        return x - y

    body = page_template % {
        'label_series': _("Series"),
        'series': series_name,
        'series_books': "\n".join([s[1] for s in sorted(series_books, sort_func)])
        }

    return body, [], _(u"Series “%s”") % series_name

def render_stat_page(link_text, generated_files_dir, arguments):
    stats_pages = {
        'general': render_stats_general_page,
        'genres': render_stats_genres_page
        }
    
    if link_text in stats_pages:
        return stats_pages[link_text](link_text, generated_files_dir, arguments)
    else:
        return None, None, None

def render_stats_general_page(link_text, generated_files_dir, arguments):
    """
    Display general stats page
    """
    page_template = html.get_html_template("stats")
    stats_item_template = html.get_html_template("stats_general_item")

    general_stats_items = (
        (_("Total books"), 'total_books'),
        (_("Total authors"), 'total_authors'),
        (_("Total genres"), 'total_genres'),

        )
    
    stats_data = c.get_general_stats()
    general_stats = []
    css_classes = ('odd', 'even')
    
    i = 0
    for label, key in general_stats_items:
        i += 1
        data = ""
        if stats_data.has_key(key):
            data = stats_data[key]

        item = stats_item_template % {
            'css_class': css_classes[i%2],
            'label': label,
            'value': data
            }
        general_stats.append(item)
        
    
    body = page_template % {
        'label_general': _("General stats"),
        'label_title': _("Collection statistics"),
        'label_detailed': _("Detailed stats"),
        'label_genres': _("Genres"),
        'stats_items': "\n".join(general_stats)
        }
    
    
    return body, [], _("General stats")
    
def render_stats_genres_page(link_text, generated_files_dir, arguments):
    """
    Render detailed genres stats page
    """
    page_template = html.get_html_template("stats_genres")
    genre_template = html.get_html_template("stats_genres_genre")
    
    genres_stat = c.get_genres_stat()
    genres_list = []
    
    for genre_name, num_books in genres_stat:
        item = genre_template % {
            'genre_name': genre_name,
            'genre_title': genres.expand_genre(genre_name),
            'num_books': num_books
            }
        genres_list.append(item)
        
    body = page_template % {
        'label_genres_stats': _("Genres stats"),
        'label_genres_authors': _("Genre name / number of books in this genre"),
        'genres_list': "\n".join(genres_list)
        }
    
    return body, [], _("Genres stats")
    
def render_set_book_rating_page(link_text, generated_files_dir, arguments):
    rating = link_text
    file_id = arguments['id']
    c.set_book_rating(file_id, rating)
    
    return render_book_page(file_id, generated_files_dir, arguments)
   
def render_export_book(link_text, generated_files_dir, arguments):
    
    export_book(link_text)
    return None, [], None

def render_read_book(link_text, generated_files_dir, arguments):
    file_id = link_text

    log.debug("Read book")
    book_file = c.get_book_file(file_id)
    book_title = c.get_book_title(file_id)
    
    # detect plugin
    try:
        plugin = get_plugin(find_plugin_for_file(book_file))
    except exceptions.PluginNotFoundError, e:
        core.error(_("Unable to read file `%s', plugin for this type of file not found.") % (su.str2unicode(book_file)))
        return None, [], None
    
    res = plugin.read_book(book_file, core.top_parent)
    
    if isinstance(res, list):
        # first item in the list is always main html file
        html_file = res[0]
    else:
        return None, [], None
    
    return ("file", html_file), [], None
 
def book_context_handler(arguments, open_html_file_cb, add_bookmark_cb):
    """
    Display book context popup menu
    """
    #global __book_popup_menu_items, __book_popup_menu
    
    try:
        file_id = int(arguments['id'])
    except:
        log.error("Invalid value `%s' for book popup menu request" % args["id"])
        return
    
    book_file = c.get_book_file(file_id)
    book_title = c.get_book_title(file_id)
    
    # detect plugin
    try:
        plugin = get_plugin(find_plugin_for_file(book_file))
    except exceptions.PluginNotFoundError, e:
        core.error(_("Unable to read file `%s', plugin for this type of file not found.") % (su.str2unicode(book_file)))
        return

    ###book_popup_menu_file_id = file_id 
    __book_popup_menu.set_data("file_id", file_id)
    __book_popup_menu.set_data("open_html_file_cb", open_html_file_cb)
    __book_popup_menu.set_data("add_bookmark_cb", add_bookmark_cb)
    # change title of some menu items before displaying
    update_locked = c.is_book_locked(file_id)
    if update_locked is None:
        return False
    
    if update_locked:
        lock_book_title = _("Unlock book")
        lock_action = False
    else:
        lock_book_title = _("Lock book")
        lock_action = True
        
    __book_popup_menu_items['lock_book'].get_data("label").set_text(lock_book_title)
    __book_popup_menu_items['lock_book'].set_data("lock_action", lock_action)
    
    # detect is book readable
    if plugin.can_edit_book(book_file):
        __book_popup_menu_items['edit_book'].set_sensitive(True)
    else:
        __book_popup_menu_items['edit_book'].set_tooltip_text(_("Plugin doesn't support this file editing"))
        __book_popup_menu_items['edit_book'].set_sensitive(False)
        
    # update list of book shelves
    # find all shelves for this book
    recreate_shelves_submenu()
    book_shelves = list()
    for m in c.get_book_metadata(file_id):
        try:
            if m['attr_name']=="shelf":
                book_shelves.append(m['attr_value'])
                
        except KeyError:
            continue
        
    shelves_submenu = __book_popup_menu_items['bookshelves_submenu'].get_submenu()
    for mi in shelves_submenu.get_children():
        shelf = mi.get_data("shelf")
        if shelf is None:
            continue
            
        icon = mi.get_data("icon")
        if shelf in book_shelves:
            icon.set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
            mi.set_tooltip_text(_(u"Remove book from the shelf “%s”") % shelf)
        else:
            icon.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
            mi.set_tooltip_text(_(u"Place book to the shelf “%s”") % shelf)
    
    log.debug("display 'book' popup menu")
    res = __book_popup_menu.popup(None, None, 
        None, 
        0, # button doesn't matter
        gtk.get_current_event_time()
        )
    print res

# Menu event handlers
def on_menu_export_book(menuitem):
    file_id = __book_popup_menu.get_data("file_id")
    
    export_book(file_id)
    
    return True

def on_book_shelf_activated(menuitem):
    file_id = __book_popup_menu.get_data("file_id")
    shelf = menuitem.get_data("shelf")
    
    found = False
    # find and toggle "shelf" metadata block
    metadata = list()
    for m in c.get_book_metadata(file_id):
        try:
            if m['attr_name']=="shelf" and m['attr_value']==shelf:
                found = True
                continue

            metadata.append(m)
        except KeyError:
            continue
        
    if not found:
        metadata.append(ca("shelf", shelf))
        
    c.set_book_metadata(file_id, metadata)
        
def on_book_shelf_new_activated(menuitem):
    """
    Display simple dialog prompt with new shelf name and ut the book on that shelf.
    """
    file_id = __book_popup_menu.get_data("file_id")
    
    shelf = core.string_prompt(_("Enter book shelf name"), _("Enter name of new or existing book shelf"))
    
    if shelf is not None:
        if shelf=="":
            return
        
        found = False
        # find and toggle "shelf" metadata block
        metadata = list()
        for m in c.get_book_metadata(file_id):
            try:
                # always add
                metadata.append(m)
    
                if m['attr_name']=="shelf" and m['attr_value']==shelf:
                    found = True
                    continue
            except KeyError:
                continue
            
        if not found:
            metadata.append(ca("shelf", shelf))
            
        c.set_book_metadata(file_id, metadata)
        recreate_shelves_submenu()
    
    
def on_menu_edit_book_description(menuitem):
    file_id = __book_popup_menu.get_data("file_id")

    rating = c.get_book_rating(file_id)
    c.set_book_rating(file_id, 0)
    if editmetadata.run_dialog(file_id):
        c.set_book_rating(file_id, rating)

def on_edit_book_file(menuitem):
    file_id = __book_popup_menu.get_data("file_id")

    book_file = c.get_book_file(file_id)
    book_title = c.get_book_title(file_id)
    
    # detect plugin
    try:
        plugin = get_plugin(find_plugin_for_file(book_file))
    except exceptions.PluginNotFoundError, e:
        core.error(_("Unable to read file `%s', plugin for this type of file not found.") % (su.str2unicode(book_file)))
        return
    
    rating = c.get_book_rating(file_id)
    c.set_book_rating(file_id, 0)
    if plugin.edit_book(book_file, core.top_parent):
        # update collection: re-read book details
        c.update_one_file(file_id)
        c.set_book_rating(file_id, rating)

def on_menu_set_book_lock(menuitem):
    file_id = __book_popup_menu.get_data("file_id")
    lock_action = menuitem.get_data("lock_action")
    c.set_book_lock(file_id, lock_action)
    
def on_menu_read_book(menuitem):
    file_id = __book_popup_menu.get_data("file_id")
    open_html_file_cb = __book_popup_menu.get_data("open_html_file_cb")

    log.debug("Read book")
    book_file = c.get_book_file(file_id)
    book_title = c.get_book_title(file_id)
    
    # detect plugin
    try:
        plugin = get_plugin(find_plugin_for_file(book_file))
    except exceptions.PluginNotFoundError, e:
        core.error(_("Unable to read file `%s', plugin for this type of file not found.") % (su.str2unicode(book_file)))
        return
    
    res = plugin.read_book(book_file, core.top_parent)
    
    if isinstance(res, list):
        # first item in the list is always main html file
        html_file = res[0]
        open_html_file_cb(html_file, page_files=res, page_title=book_title)
        # FIXME: replace "readbook://" with another schema prefix?
        #self.history.go_to("readbook://%s" % file_id, path=html_file,
        #    page_files=res, title=book_title)

def on_menu_bookmark_book(menuitem):
    file_id = __book_popup_menu.get_data("file_id")
    add_bookmark_cb = __book_popup_menu.get_data("add_bookmark_cb")
    book_title = c.get_book_title(file_id)
    add_bookmark_cb("book://%s" % file_id, _(u"Book “%s”") % book_title)
    
def export_book(file_id):
    
    try:
        book_file = c.get_book_file(file_id)
        
        # book_file is locale encoded string!
        book_file = str(book_file)
        if not os.path.isfile(book_file):
            raise exceptions.BrowserError(_("Book file (%s) doesn't exist." % su.str2unicode(book_file)))

        # extract filename from full path
        _head, filename = os.path.split(book_file)

        # get saved destination path that will be used as start directory in the dialog
        saved_dir = settings.get_str("paths/file_save_target_dir")

        # display dialog window with destination copy request
        dialog = gtk.FileChooserDialog(_("Choose the place where you want to save book"),
            parent=core.top_parent, action=gtk.FILE_CHOOSER_ACTION_SAVE,
            buttons=None, backend=None)

        dialog.add_button(_("Cancel"), gtk.RESPONSE_CANCEL)
        dialog.add_button(_("Save"), gtk.RESPONSE_ACCEPT)
        dialog.set_do_overwrite_confirmation(True)
        dialog.set_modal(True)
        dialog.set_local_only(True)
        dialog.set_current_name(su.str2unicode(filename)) # filename is NOT unicode string
        dialog.set_current_folder(saved_dir)

        response = dialog.run()

        target_path = dialog.get_filename()
        dialog.destroy()

        if gtk.RESPONSE_ACCEPT == response and not target_path is None:
            # save file to desired location
            shutil.copyfile(book_file, target_path)

            # remember directory where file was saved
            saved_dir = os.path.split(target_path)[0]
            settings.set_str("paths/file_save_target_dir", saved_dir)

    except ValueError:
        core.error(_("Invalid file_id provided: `%s'") % file_id_str)

    except IOError, e:
        core.error(_("IOError occured for file `%(file)s': %(error)s") % {
            'file': e.filename, 
            'error': su.str2unicode(e.strerror)
            })
        
    except exceptions.BrowserError, e:
        core.error(e.message)
    
renderers = {
     'book': render_book_page,
     'genre': render_genre_page,
     'author': render_author_page,
     'series': render_series_page,
     'stat': render_stat_page,
     'voteforbook': render_set_book_rating_page,
     'export': render_export_book,
     'readbook': render_read_book,
     }

context_handlers = {
    'book': book_context_handler,
    }
